#ifndef __machine_h__
#define __machine_h__

#include <string>
#include <vector>

#include <boost/bind.hpp>
#include <boost/function.hpp>

class Predictor_base;
class Output_base;
class Arena_base;
class Predictor_link;
class Model;
class IConnection;
class OConnection;
class Trace;

using namespace std;

class Machine
{
 public:
  Machine();
  ~Machine();

  void init( void );

  void update_arena( float t );
  void prepare_arena(void );

  void step( float t );

  void predict( float t );
  void update_input( float t );

  void activate_models( float t );
  void activate_model_output_links( void );

  void save_state( float t );

  void add_model( Model *m );
  Model *get_model( int i );
  vector<Model *> *get_models();

  Arena_base *get_arena( void );
  void set_arena( Arena_base *arena );

  void reset( void );

  void add_output_connection( OConnection *oc );
  void add_input_connection( IConnection *ic );

  OConnection *create_output_connection( Model *m, boost::function<void( float )> f );
  OConnection *create_output_connection( Model *m, boost::function<void( float )> f, string name, Output_base *output_module );
  IConnection *create_input_connection( Predictor_base *p, int input_index, float *source);

  vector<OConnection *> &get_output_connections();
  vector<IConnection *> &get_input_connections();

  void set_trace(Trace *trace);
  void remove_trace(void);
  void update_trace(double t);

 private:
  vector<Model *> _models;

  Arena_base *_arena;

  vector<IConnection *> _input_connections;
  vector<OConnection *> _output_connections;

  float _previous_time;

  // when non-null, input data will be recorded in this trace
  Trace *_trace;
};

#endif
